﻿// -----------------------------------------------------------------------
// <copyright file="Runner.cs" company="Microsoft">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Proto
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class Runner
    {
        private const int LOOPS_PER_SECOND = 100;
        private readonly Thread m_Runner;
        private Queue<Action> m_Work;
        private Queue<Action> m_StagingQueue;
        private Dictionary<IRequiresService, IRequiresService> m_Staging;
        private Dictionary<IRequiresService, IRequiresService> m_Dirty;
        private bool? m_IsStopRequested;

        public Thread Thread { get
        {
            return m_Runner;
        } }
   
        internal Runner( String name )
        {
            m_Runner = new Thread( this.Run ) { Name = name };
            m_Work = new Queue<Action>();
            m_StagingQueue = new Queue<Action>();
            m_Dirty = new Dictionary<IRequiresService,IRequiresService>();
            m_Staging = new Dictionary<IRequiresService, IRequiresService>();
        }

        public void Start()
        {
            if (m_IsStopRequested != null) return;
            m_IsStopRequested = false;
            m_Runner.Start();
        }

        public void Stop( TimeSpan timeout )
        {
            m_IsStopRequested = true;
            m_Runner.Join( timeout );
        }

        private void Run()
        {
            while (m_IsStopRequested == false )
            {
                if( m_Dirty.Count > 0 || m_Staging.Count > 0)
                {
                    Dictionary<IRequiresService, IRequiresService> working = m_Staging;
                    IRequiresService[] reqs = new IRequiresService[working.Count];
                    working.Values.CopyTo( reqs,0 );

                    foreach( IRequiresService item in reqs )
                    {
                        if (item == null) continue;
                        item.RecieveProcessing();
                    }
                    m_Staging.Clear();
                    m_Staging = Interlocked.Exchange(ref m_Dirty, m_Staging);
                }
                if( m_Work.Count > 0 || m_StagingQueue.Count > 0)
                {
                    Queue<Action> work = m_StagingQueue;
                    while (work.Count > 0)
                    {
                        Action action = work.Dequeue();
                        if (action != null) action();
                    }

                    m_StagingQueue = Interlocked.Exchange(ref m_Work, m_StagingQueue); ;
                }

                Thread.Sleep(1000/LOOPS_PER_SECOND);
            }
        }
        
        public void Enqueue( Action work )
        {

            m_Work.Enqueue( work );
        }

        /// <summary>
        /// Adds a weak reference based on RequestService event and begins scheduling
        /// service.
        /// </summary>
        /// <param name="register"></param>
        public void Register( IRequiresService register )
        {
            WeakReference weak = new WeakReference(register);

            Action<IRequiresService> action = null;
            action = delegate(IRequiresService service)
                    {
                        if( weak.IsAlive )
                        {
                            this.HandleServiceRequest((IRequiresService)weak.Target);
                        }
                        else
                        {
                            if (action == null) throw new Exception();
                            register.RequestService -= action;
                        }
                    };

            register.RequestService += action;
        }

        private void HandleServiceRequest(IRequiresService requires_service)
        {
            m_Dirty[requires_service] = requires_service;
        }
    }

    public sealed class WeakEventHandler<TEventArgs> 
    {
        private readonly WeakReference _targetReference;
        private readonly MethodInfo _method;

        public WeakEventHandler(Action<TEventArgs> callback)
        {
            _method = callback.Method;
            _targetReference = new WeakReference(callback.Target, true);
        }

        [DebuggerNonUserCode]
        public void Handler(TEventArgs sender)
        {
            var target = _targetReference.Target;
            if (target != null)
            {
                var callback = (Action<TEventArgs>)Delegate.CreateDelegate(typeof(Action<TEventArgs>), target, _method, true);
                if (callback != null)
                {
                    callback(sender);
                }
            }
        }
    }
}
